use common_base::{
    futures::TryStreamExt,
    prelude::*,
    sqlx::{self, mysql::MySqlArguments, Arguments, MySql},
};
use common_core::{
    data::{FctMetadata, Metadata, MetadataType},
    ServiceContextBase,
};

use super::{metadata_util::MetadataUtil, MetadataManager};

pub struct FctMetadataManager;

impl FctMetadataManager {
    pub async fn get_metadata_model(
        &self,
        srv_ctx: &mut ServiceContextBase,
        fct_id: &str,
    ) -> Result<Option<FctMetadata>> {
        let mut fct_metadata = self.load_sys_facts(srv_ctx, fct_id).await?;

        if let Some(fct_metadata) = fct_metadata.as_mut() {
            // 装入数据字典的对象定义
            self.load_do_metadata(srv_ctx, fct_metadata).await?;
            // 装入数据字典的外键引用数据字典
            self.load_fkey_dct(srv_ctx, fct_metadata).await?;
            // self.load_ref_fct
            // self.load_sys_dct_grp
            // todo!()
        }
        Ok(fct_metadata)
    }

    async fn load_sys_facts(
        &self,
        srv_ctx: &mut ServiceContextBase,
        fct_id: &str,
    ) -> Result<Option<FctMetadata>> {
        let mut arguments = MySqlArguments::default();
        arguments.add(fct_id);

        let (sql, arguments) = MetadataUtil::generate_sql_and_args(
            srv_ctx,
            FctMetadata::SYS_FACTS,
            "*",
            "OBJ_ID=?",
            "",
            arguments,
            fct_id,
        );

        let mut rows = sqlx::query_with::<MySql, MySqlArguments>(sql.as_str(), arguments).fetch(
            srv_ctx
                .get_connection_mut()
                .expect("can not find db_conn in context"),
        );

        while let Some(row) = rows.try_next().await? {
            return Ok(Some(FctMetadata::new(fct_id, row.try_into()?)));
        }
        Ok(None)
    }

    async fn load_do_metadata(
        &self,
        srv_ctx: &mut ServiceContextBase,
        fct_metadata: &mut FctMetadata,
    ) -> Result<()> {
        // 装入此数据字典的数据对象元数据
        let do_id = fct_metadata.get_object_id().to_owned();
        if let Some(metadata) = MetadataManager::get_instance(MetadataType::Do)
            .get_metadata(srv_ctx, do_id.as_str())
            .await?
        {
            metadata
                .as_do_metadata_mut()
                .unwrap()
                .set_fct_metadata(fct_metadata.get_object_id());

            fct_metadata.set_do_metadata(metadata);
        }

        Ok(())
    }

    async fn load_fkey_dct(
        &self,
        srv_ctx: &mut ServiceContextBase,
        fct_metadata: &mut FctMetadata,
    ) -> Result<()> {
        if let Some(do_metadata) = fct_metadata.get_do_metadta() {
            let fkey_dct_metadata_ids = do_metadata.as_do_metadata().unwrap().get_fkey_metadatas();
            fct_metadata.set_fkey_dct_metadatas(fkey_dct_metadata_ids);
            Ok(())
        } else {
            Err(ErrorCode::IllegalStateError(format!(
                "can not find DoMetadata for FctMetadata[object_id: {}]",
                fct_metadata.get_object_id(),
            )))
        }
    }

}
